using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using RPGOnline.Filters;
using RPGOnline.Remoting;
using RPGOnline.Remoting.Entities;
using RPGOnline.Remoting.Events;

namespace RPGOnline.Controls.Chat
{
    public class ChatControlPresenter
    {
        private readonly SessionContext m_sessionContext;
        private readonly UserManager m_userManager;
        private readonly ChatManager m_chatManager;
        private readonly ChatControl m_chatControl;
        private readonly DiceRoller m_diceRoller;
        private readonly PrivateMessanger m_privateMessanger;
        private User m_systemUser;
        private GroupManager m_groupManager;
        private List<IMessageHandler> m_messageHandlers = new List<IMessageHandler>();
        private ConfigurationHandler m_configurationHandler;

        public ChatControlPresenter(SessionContext sessionContext, UserManager userManager, ChatManager chatManager, ChatControl chatControl)
        {
            m_sessionContext = sessionContext;
            m_userManager = userManager;
            m_chatManager = chatManager;
            m_chatControl = chatControl;
            m_diceRoller = new DiceRoller(userManager);
            m_groupManager = new GroupManager(sessionContext, userManager, chatManager);
            m_privateMessanger = new PrivateMessanger(chatManager, m_sessionContext, userManager);
            m_configurationHandler = new ConfigurationHandler(m_userManager);
            m_systemUser = new User("System", chatControl.Font, Color.Gray);

            m_messageHandlers.Add(m_diceRoller);
            m_messageHandlers.Add(m_privateMessanger);
            m_messageHandlers.Add(m_configurationHandler);

            m_chatControl.SetPresenter(this);
            m_chatControl.SetUserColor(m_userManager.CurrentUser.FontColor);

            m_groupManager.OnGroupAdded += (s, e) => m_chatControl.Update(m_sessionContext);
            m_groupManager.OnGroupRemoved += (s, e) => m_chatControl.Update(m_sessionContext);
            m_groupManager.OnUserInfoChanged += (s, e) => m_chatControl.Update(m_sessionContext);
            m_groupManager.OnUserAddedToGroup += (s, e) => m_chatControl.Update(m_sessionContext);
            m_groupManager.OnUserRemovedFromGroup += (s, e) => m_chatControl.Update(m_sessionContext);

            m_chatManager.OnUserRegistered += (s, e) => m_chatControl.AppendMessage(m_systemUser, null, string.Format("{0} entered the chat", e.User.Name));
            m_chatManager.OnUserUnregistered += (s, e) => m_chatControl.AppendMessage(m_systemUser, null, string.Format("{0} leaved the chat", e.User.Name));
            m_chatManager.OnUserRolled += (s, e) => m_chatControl.AppendMessage(m_systemUser, null, string.Format("{0} rolled {1}", e.User.CharacterName ?? e.User.Name, e.RollResult));
            m_chatManager.OnMessageReceived += OnMessageReceived;
            m_chatManager.OnPrivateMessageReceived += OnPrivateMessageReceived;
            m_chatManager.OnUserMovedToGroup += OnUserMovedToGroup;
            m_chatManager.OnSessionUpdated += (s, e) =>
                                                  {
                                                      m_sessionContext.Update(e.SessionContext);
                                                      m_chatControl.Update(m_sessionContext);
                                                  };

            m_diceRoller.OnUserRoll += (s, e) => m_chatManager.Notify.Roll(e.User, e.RollResult);
            m_diceRoller.OnUserSecretRoll += (s, e) => m_chatControl.AppendMessage(m_systemUser, null, string.Format("{0} secretly rolled {1}", e.User.CharacterName ?? e.User.Name, e.RollResult));
            m_diceRoller.OnMasterRoll += (s, e) => m_chatManager.Notify.Roll(e.User, e.RollResult);

            m_privateMessanger.OnSendPrivateMessage += (s, e) => m_chatManager.Notify.SendPrivateMessage(e.User, e.DestinationUser, e.Message);
            m_privateMessanger.OnSendGroupMessage += (s, e) => m_chatManager.Notify.SendMessage(e.ChatGroup.Id, e.User, e.Message);

            m_configurationHandler.OnCharacterNameChanged += (s, e) => m_chatManager.Notify.UserInfoChanged(e.User);
        }

        public bool IsMaster
        {
            get { return m_userManager.IsMaster; }
        }

        private void OnUserMovedToGroup(object sender, UserMovedToGroupEventArgs e)
        {
            if (e.User.Equals(m_userManager.CurrentUser))
                m_groupManager.DefaultChatGroup = m_sessionContext.FindGroupById(e.GroupId);
        }

        private void OnPrivateMessageReceived(object sender, PrivateMessageReceivedEventArgs e)
        {
            m_chatControl.AppendPrivateMessage(e.SenderUser, e.DestinationUser, e.Message);
        }

        private void OnMessageReceived(object sender, MessageReceivedEventArgs e)
        {
            if (m_userManager.IsMaster || m_groupManager.FindChatGroupByUser(m_userManager.CurrentUser).Id == e.ChatGroupId || e.ChatGroupId == Guid.Empty || e.User.Equals(m_userManager.CurrentUser))
            {
                ChatGroup chatGroup = m_sessionContext.FindGroupById(e.ChatGroupId);
                m_chatControl.AppendMessage(e.User, chatGroup, e.Message);
            }
        }

        public void SendMessage(string message)
        {
            foreach (IMessageHandler messageHandler in m_messageHandlers)
            {
                if (!messageHandler.IsValid(message)) 
                    continue;

                if(messageHandler.Handle(message))
                    return;
            }
            
            m_chatManager.Notify.SendMessage(m_groupManager.DefaultChatGroup.Id, m_userManager.CurrentUser, message);
        }

        public void Roll(int howMany, int diceSides, int modifiers)
        {
            RollResult rollResult = m_diceRoller.Roll(howMany, diceSides, modifiers);
            m_chatManager.Notify.Roll(m_userManager.CurrentUser, rollResult);
        }

        public void UserColorChanged(Color color)
        {
            m_userManager.CurrentUser.FontColor = color;
            m_chatManager.Notify.UserInfoChanged(m_userManager.CurrentUser);
        }

        public void CreateGroup()
        {
            CreateChatGroup createChatGroup = new CreateChatGroup(m_chatManager, m_sessionContext);
            createChatGroup.ShowDialog(m_chatControl);
        }

        public ChatGroup[] GetMoveToGroupsForUser(User user)
        {
            return m_sessionContext.Groups.Where(g => !g.Contains(user.Id)).ToArray();
        }

        public void MoveUserToGroup(User user, ChatGroup chatGroup)
        {
            m_chatManager.Notify.MoveUserToGroup(user, chatGroup.Id);
        }

        public void SetDefaultChatGroupTarget(ChatGroup chatGroup)
        {
            m_groupManager.DefaultChatGroup = chatGroup;
            m_chatControl.Update(m_sessionContext);
        }

        public bool IsDefaultPrivateMessageTarget(User user)
        {
            return m_privateMessanger.DefaultChatUser != null ? m_privateMessanger.DefaultChatUser.Equals(user) : false;
        }

        public bool IsDefaultChatMessageTarget(ChatGroup chatGroup)
        {
            return m_groupManager.DefaultChatGroup != null ? m_groupManager.DefaultChatGroup.Id == chatGroup.Id : false;
        }

        public bool IsCurrentUser(User user)
        {
            return m_userManager.Equals(user);
        }

        public void SetDefaultPrivateMessageTarget(User user)
        {
            m_privateMessanger.DefaultChatUser = user;
            m_chatControl.Update(m_sessionContext);
        }

        public bool IsCurrentUserInGroup(ChatGroup chatGroup)
        {
            return m_groupManager.FindChatGroupByUser(m_userManager.CurrentUser).Id == chatGroup.Id;
        }
    }
}